// Copyright 2023 The Perses Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package auth

import (
	"fmt"
	"net/http"

	"github.com/labstack/echo/v4"
	"github.com/perses/perses/internal/api/crypto"
	databaseModel "github.com/perses/perses/internal/api/database/model"
	"github.com/perses/perses/internal/api/interface"
	"github.com/perses/perses/internal/api/interface/v1/user"
	"github.com/perses/perses/internal/api/route"
	"github.com/perses/perses/internal/api/utils"
	"github.com/perses/perses/pkg/model/api"
	"github.com/zitadel/oidc/v3/pkg/oidc"
	"golang.org/x/oauth2"
)

type nativeEndpoint struct {
	dao             user.DAO
	jwt             crypto.JWT
	tokenManagement tokenManagement
}

func (e *nativeEndpoint) GetExtraProviderLogoutHandler() echo.HandlerFunc {
	return nil // No specific logout handler for native auth
}

func (e *nativeEndpoint) GetAuthKind() string {
	return utils.AuthKindNative
}

func (e *nativeEndpoint) GetSlugID() string {
	return "" // no slug ID needed for native auth
}

func newNativeEndpoint(dao user.DAO, jwt crypto.JWT) authEndpoint {
	return &nativeEndpoint{
		dao:             dao,
		jwt:             jwt,
		tokenManagement: tokenManagement{jwt: jwt},
	}
}

func (e *nativeEndpoint) CollectRoutes(g *route.Group) {
	g.POST(fmt.Sprintf("/%s/%s", utils.AuthKindNative, utils.PathLogin), e.auth, true)
}

func (e *nativeEndpoint) auth(ctx echo.Context) error {
	body := &api.Auth{}
	if err := ctx.Bind(body); err != nil {
		return apiinterface.HandleBadRequestError(err.Error())
	}
	usr, err := e.dao.Get(body.Login)
	if err != nil {
		if databaseModel.IsKeyNotFound(err) {
			return apiinterface.HandleBadRequestError("wrong login or password ")
		}
		return apiinterface.InternalError
	}

	if !crypto.ComparePasswords(usr.Spec.NativeProvider.Password, body.Password) {
		return apiinterface.HandleBadRequestError("wrong login or password ")
	}
	login := body.Login
	providerInfo := crypto.ProviderInfo{
		ProviderKind: utils.AuthKindNative,
		ProviderID:   "", // no provider ID needed for native auth
	}
	accessToken, err := e.tokenManagement.accessToken(login, providerInfo, ctx.SetCookie)
	if err != nil {
		return err
	}
	refreshToken, err := e.tokenManagement.refreshToken(login, providerInfo, ctx.SetCookie)
	if err != nil {
		return err
	}

	return ctx.JSON(http.StatusOK, oauth2.Token{
		AccessToken:  accessToken,
		RefreshToken: refreshToken,
		TokenType:    oidc.BearerToken,
	})
}
